// const Events=require('events');
const WS = require("ws");
const queue = require('queue');
const mongoose = require('mongoose');
const config=require('../config');
const configData = require('../db/gameUser_data');
class Docker{
    constructor(){
        require('../db/mongodb').load();
        this.ip=!!process.argv[2]?process.argv[2]:'127.0.0.1';
        this.port=!!process.argv[3]?parseInt(process.argv[3]):6100;
        this.type=!!process.argv[4]?parseInt(process.argv[4]):0; //类型  0代表随机乱斗服务器，1代表组队匹配服务器，2代表开房服务器
        this.currentRoomId=1;
        this.roomUserCount=2;
        this.rooms={};  //房间数据
        this.users={}; //用户数据
        this.matchingGroups=[]; //组队匹配的队伍
        this.friendRooms=[]; //好友房间
        this.orders=[];
        this.status=0; //服务器状态 0可加入 1不可加入
        this.ws=null;
        this.bots=[];
        this.listen();
        console.log('监控端口成功');
        this.createBots();
        console.log('创建机器人成功');
        this.register();
        console.log('注册成功');
        this.queue=queue({autostart:true});
        this.queue.pushTry=function(fun){
            this.queue.push(function(){
                try{
                    this.fun();
                }catch(e){
                    console.error(e);
                    const mongoose = require('mongoose');
                    const ErrorModel=mongoose.models['Error'];
                    ErrorModel.create({
                        stack:JSON.stringify(e.stack)
                    });
                }
            }.bind({fun:fun}))
        }.bind(this);
        //每隔5秒自动运行
        setInterval(function(){
            this.queue.pushTry(async function(){await this.autoRun();}.bind(this));
        }.bind(this),12000);
        //给用户发送心跳
        setInterval(function(){
            let keys=Object.keys(this.users);
            for(let i=0;i<keys.length;i++){
                let user=this.users[keys[i]];
                user.sendString('{"e":"h"}',false);
            }
        }.bind(this),1000);
    }
    //定期自动清理超时房间，超时用户，保存docker数据到数据库
    async autoRun(){
        //自动清理超时房间
        let keys=Object.keys(this.rooms);
        let now=new Date().getTime();
        for(let i=0;i<keys.length;i++){
            if(this.rooms[keys[i]].createTime<now-1200000){
                this.rooms[keys[i]].gameOver();
            }
        }
        //清理超时用户
        keys=Object.keys(this.users);
        for(let i=0;i<keys.length;i++){
            if(this.users[keys[i]].lastUpdateTick<now-3600000&&!this.users[keys[i]].isBot){
                console.log('超时清理用户释放内存:',this.users[keys[i]].nickname);
                if(!!this.users[keys[i]].friendRoom){
                    this.users[keys[i]].friendRoom.exit(this.users[keys[i]]);
                }
                delete this.users[keys[i]];
            }
        }

        // 匹配玩家上下波动不超过1个大段位
        let orders = [...this.orders];
        orders.sort( (a, b) => a.level - b.level);
        this.orders = [];
        let index = -1; // 起始玩家段位下标
        let len = orders.length;
        let levelArr = configData.levelArr;
        // console.log('orders =', orders.length);
        for(let i = len - 1; i >= 0; i--){
            console.log('level =', orders[i].user.level);
            let num = orders.length - 1 - i; // 相匹配人数
            let _index = levelArr.findIndex( item => item <= orders[i].user.level);
            if(index == -1 && i != 0){
                index = _index;
                continue;
            }
            let flag = index == -1 && i == 0;
            if(flag) {
                num = 1;
                index = _index;
            }
            // 没有足够与之匹配的玩家则加入机器人
            if((_index - index) > 1 || flag || i == 0){
                let fun = async function (ind, _num, arr){
                    if(!arr.length)return;
                    let min = ind == 0 ? 0 : (ind - 1);
                    let max = (ind == levelArr.length - 1) ? (levelArr.length - 1) : (ind + 1);
                    let botCount = this.type == 0 ? (1 + Math.floor(Math.random() * (5 - _num))) : (6 - _num);
                    console.log('botCount =', botCount, _num);
                    for(let i = 0; i < botCount; i++){
                        let bot = this.getABot(levelArr[min], levelArr[max]);
                        if(!bot) continue;
                        arr.push({user: bot, tick: arr[0].tick});
                    }
                    // 开房匹配
                    await this.readyRoom([...arr]);
                }.bind(this);
                // console.log('i====>>>', i, index, _index, flag);
                if(i== 0 && ((_index - index)  <= 1) && !flag) num += 1; // 剩余的玩家相匹配
                let orderArr = orders.splice((flag || i == 0 && (_index - index <= 1)) ? 0 : i+1);
                await fun(index, num, orderArr);
                index = _index;
                if(i == 0 && !flag && orders.length){
                    await fun(index, 0, orders.splice(0));
                }
                continue;
            }
            // 相匹配
            if(num < 6 && i != 0) continue;
            index = -1;
            // 人数够6人，开房匹配
            let _orderArr = orders.splice(i);
            await this.readyRoom(_orderArr);
        }

        // //自动将随机乱斗队列中的用户匹配入房
        // //把5秒之前的匹配用户拿出来进行匹配this.type===0&&!this.orders.find(item=>{return !!item.user.isBot;})
        // if(keys.length<200&&this.orders.length>0&&this.orders.length<=5){
        //     //如果是随机乱斗，则已经又机器人了不再添加机器人
        //     if(this.type===0&&!!this.orders.find(item=>{return !!item.user.isBot;})){
        //
        //     }else{
        //         this.bots.sort((a,b)=>{return Math.random()-0.5;});
        //         let botCount=this.type===0?1+Math.floor(Math.random()*(5-this.orders.length)):6-this.orders.length;
        //         console.log('自动机器人加入',botCount,this.orders.length);
        //         for(let i=0;i<botCount;i++){
        //             let bot=this.getABot();
        //             if(!!bot){
        //                 let order=this.joinOrder(bot);
        //                 order.tick=this.orders[0].tick;
        //             }
        //         }
        //     }
        // }
        // if(this.orders.length>=2){
        //     let now=new Date().getTime();
        //     let orders=[]; //需要进入匹配的用户
        //     let leftOrders=[]; //没有超过5秒的剩余用户
        //     for(let i=0;i<this.orders.length;i++){
        //         // if(now-this.orders[i].tick>5000){
        //              orders.push(this.orders[i]);
        //         // }else{
        //             //leftOrders.push(this.orders[i]);
        //        // }
        //     }
        //     //按照分数高低进行排序
        //     orders.sort((a,b)=>{return b.user.level-a.user.level;});
        //     //如果是随机匹配房，则计算需要的房间数方式不一样
        //     let roomCount=Math.ceil(orders.length/6);
        //     for(let i=0;i<roomCount;i++){
        //         if(orders.length<2){
        //             break;
        //         }
        //         let aRoomOrders=[];//orders.length>=6?orders.splice(0,6):orders.splice(0,4);
        //         if(this.type===1){ //如果是随机组队的模式，必须是4人进房，或者6人进房
        //             if(orders.length<4){
        //                 console.log('人数不够，中断匹配',orders.length);
        //                 break;
        //             }else{
        //                 aRoomOrders=orders.length<=5?orders.splice(0,4):orders.splice(0,6);
        //             }
        //         }else{
        //             aRoomOrders=orders.splice(0,6);
        //             console.log('人数够，开始匹配');
        //         }
        //         let roomObj=null;
        //         if(this.type===0){
        //             roomObj={type:0,roomType:0};
        //         }else{
        //             roomObj={type:1,roomType:1};
        //         }
        //         //如果进入房间的人数小于2人，直接中断
        //         if(aRoomOrders.length<2){
        //             break;
        //         }
        //         let room=this.createARoom(roomObj);
        //         for(let j=0;j<aRoomOrders.length;j++){
        //             if(this.type===0){
        //                 aRoomOrders[j].user.group=j;
        //             }else{
        //                 aRoomOrders[j].user.group=j%2;
        //             }
        //             aRoomOrders[j].user.joinRoom(room);
        //         }
        //         await room.gameStart();
        //     }
        //     //剩余的单个用户继续进入匹配队列
        //     this.orders=[].concat(orders,leftOrders);
        // }

        //保存服务器信息到数据库
        const DockerModel=mongoose.models['Docker'];
        let userKeys=Object.keys(this.users);
        let count=0;
        for(let i=0;i<userKeys.length;i++){
            if(!this.users[userKeys[i]].isBot){//(!!this.users[userKeys[i]].socket&&this.users[userKeys[i]].socket.readyState===1){
                count++;
            }
        }
        DockerModel.updateOne({ip:this.ip,port:this.port},{$set:{onlineUserCount:count}}).exec();
    }

    async readyRoom(orderArr){
        let roomObj = this.type == 0 ? {type: 0, roomType: 0} : {type: 1, roomType: 1};
        //如果进入房间的人数小于2人，直接中断
        if(orderArr.length < 2) return;
        let room = this.createARoom(roomObj);
        for(let j = 0; j < orderArr.length; j++){
            orderArr[j].user.group = this.type == 0 ? j : j % 2;
            orderArr[j].user.joinRoom(room);
        }
        await room.gameStart();
    }

    //随机获取一组用户用于匹配时显示
    getRandomUsers(){
        let keys=Object.keys(this.users);
        keys.sort((a,b)=>{return Math.random() - 0.5;});
        let result=[];
        for(let i=0;i<5;i++){
            if(!!keys[i]){
                result.push(this.users[keys[i]].getMessage());
            }
        }
        return result;
    }
    //创建一群机器人
    async createBots(){
        const GameUserModel=mongoose.models['GameUser'];
        let botGameUsers=await GameUserModel.find({status:2});
        const Bot=require('../game/Bot');
        for(let i=0;i<botGameUsers.length;i++){
            let botGameUserMessage=botGameUsers[i].getMessage();
            let bot=new Bot(botGameUsers[i].getMessage(),this);
            bot.role=botGameUserMessage.role;
            this.users[botGameUserMessage._id]=bot;
            this.bots.push(bot);
        }
        console.log('机器人创建完毕');
        //凌晨4点自动更新机器人信息
        const Cron = require('node-cron');
        Cron.schedule('0 0 4 * * *', async function(){
            await this.updateBots();
        }.bind(this));

    }
    //更新所有机器人信息
    async updateBots(){
        const GameUserModel=mongoose.models['GameUser'];
        let botGameUsers=await GameUserModel.find({status:2});
        for(let i=0;i<botGameUsers.length;i++){
            let botGameUserMessage=botGameUsers[i].getMessage();
            let bot=this.users[botGameUserMessage._id];
            if(!!bot){
                bot.nickname=botGameUserMessage.nickname;
                bot.headImgUrl=botGameUserMessage.headImgUrl;
                bot.role=botGameUserMessage.role;
                bot.sex=botGameUserMessage.sex;
            }
        }
    }

    getABot(start, end){
        this.bots.sort( (a,b)=> Math.random() - 0.5);
        let bot=this.bots.find(item=>{return !item.room&&!this.orders.find(order=>{return order.user._id===item._id;})});
        if(!!bot){
            bot.role = 0;
            bot.bombCount=Math.random()>0.5?1:2;
            bot.character=Math.floor(Math.random()*4);
            bot.thumbtackCount=Math.random()>0.1?0:1;
            bot.level = start + Math.floor(Math.random() * (end - start));
            // 根据等级区分机器人智商
            let index = configData.levelArr.findIndex( item => bot.level > item);
            bot.IQ = this.getBotIQ(index);
            bot.room = -1;
            bot.role = Math.floor(Math.random() * 11);
            bot.bombColor = Math.floor(Math.random() * 7);
            console.log('bot.level =', bot.level, Math.round(bot.IQ * 100) / 100);
        }
        return bot;
    }

    getBotIQ(index){
        let IQ = 0;
        let rand = Math.random();
        switch(index){
            case 7 : IQ = rand * 0.1; break;
            case 6 : IQ = 0.1 + rand * 0.1; break;
            case 5 : IQ = 0.2 + rand * 0.1; break;
            case 4 : IQ = 0.3 + rand * 0.2; break;
            case 3 : IQ = 0.5 + rand * 0.2; break;
            case 2 : IQ = 0.7 + rand * 0.2; break;
            case 1 : IQ = 0.9 + rand * 0.1; break;
            case 0 : IQ = 1; break;
        }
        return IQ;
    }

    //用户登陆时绑定
    async  userLogin(_id,token,socket){
        let user=null;
        if(!!this.users[_id]&&this.users[_id].token==token){
            user=this.users[_id];
        }
        if(!user){
            try{
                let GameUserModel=mongoose.models['GameUser'];
                let userData=await GameUserModel.findById(_id);
                if(!userData){
                    throw new Error('没有这个用户');
                }
                if(userData.token!=token){
                    throw new Error('token验证失败！请重新进入游戏！');
                }
                let User=require('../game/User');
                this.users[_id]=user=new User(userData.getMessage(),this);
            }catch(e){
                console.log('loginError==>', e, e.stack);
                socket.send(JSON.stringify({e:'loginError',message:e.message}));
            }
        }
        if(!!user){
            await user.initUser();
            user.bindSocket(socket);
            user.sendEvent('loginSuccess',user.getMessage());
        }
    }
    //删除一个房间
    removeRoom(roomId){
        delete this.rooms[roomId];
    }
    //小组加入匹配队列
    matchingGroupJoinOrder(groupId){
        let matchingGroup=this.matchingGroups.find(item=>{return item.groupId==groupId});
        if(!!matchingGroup){
            matchingGroup.status=1;
            matchingGroup.sendEvent('matchingGroupMatching',{});
        }
        let matchingGroups=this.matchingGroups.filter(item=>{return item.status===1&&item.users.length===matchingGroup.users.length});
        if(matchingGroups.length===2){
            let room=this.createARoom({type:1,roomType:1});
            for(let i=0;i<2;i++){
                let matchingGroupIndex=this.matchingGroups.findIndex(item=>{return item.groupId==matchingGroups[i].groupId});
                this.matchingGroups.splice(matchingGroupIndex,1);
                for(let j=0;j<matchingGroups[i].users.length;j++){
                    matchingGroups[i].users[j].matchingGroup=null;
                    matchingGroups[i].users[j].group=i;
                    matchingGroups[i].users[j].joinRoom(room);
                    //room.addUser(matchingGroups[i].users[j]);
                }
            }
            room.gameStart();
        }

    }
    //加入匹配队列
    joinOrder(user){
        let index=this.orders.findIndex(item=>{return item.user._id==user._id});
        if(index<0){
            console.log('用户开始随机乱斗匹配：',user.nickname);
            let order={user:user,tick:new Date().getTime()};
            this.orders.push(order);
            return order;
        }
    }
    //退出匹配队列
    exitOrder(user){
        let index=this.orders.findIndex(item=>{return item.user._id==user._id});
        if(index>=0){
            this.orders.splice(index,1);
            //this.sendOrdersUsers();
        }
    }
    //给匹配中的用户发送信息
    sendOrdersUsers(){
        let obj={e:'userMatching',users:[]};
        for(let i=0;i<this.orders.length;i++){
            obj.users.push(this.users[this.orders[i]].getMessage());
        }
        for(let i=0;i<this.orders.length;i++){
            this.users[this.orders[i]].send(obj);
        }
    }
    //创建一个房间并返回这个房间
    createARoom(options){
        options.roomId=this.currentRoomId;
        //实例化一个房间
        let Room=require('../game/Room');
        let room=new Room(options,this);
        this.rooms[room.roomId]=room;
        this.currentRoomId++;
        return room;
    }
    listen(){
        if(config.debug){
            this.ws = new WS.Server({
                port: this.port
            });
        }else{
            const https = require('https');
            const fs=require('fs');
            const server = new https.createServer({
                key: fs.readFileSync('./cert/ppt/2880273_ppt.073c.com.key'),
                cert: fs.readFileSync('./cert/ppt/2880273_ppt.073c.com.pem'),
            });
            this.ws = new WS.Server({server});
            server.listen(this.port);
        }

        this.ws.broadcast=function(data){
            this.ws.clients.forEach(function(client) {
                if(client.readyState == 1){
                    console.log('发送消息',data,client.id);
                    client.send(data);
                }
            });
        }.bind(this);
        this.ws.on('connection', function(socket) {
            console.log('新连接');
            socket.on('message', async function(data) {
                //console.log('收到信息：',data);
                try{
                    let dataObj=JSON.parse(data);
                    switch(dataObj.r){
                        //登陆绑定
                        case 'login':{
                            this.queue.pushTry(async function(){
                                await this.userLogin(dataObj._id,dataObj.token,socket);
                            }.bind(this));
                            break;
                        }
                        default:{
                            socket.emit('method',dataObj);
                            break;
                        }
                    }
                }catch(e){
                    console.error(e.message);
                }
            }.bind(this));
            socket.on('ping',function(data){
                socket.pong(data);
                console.log('收到ping：',data);
            });
            socket.on('pong',function(data){
                console.log('收到pong：',data);
            });
        }.bind(this));
    }
    async register(){
        let DockerModel=mongoose.models['Docker'];
        let obj={
            ip:this.ip,
            port:this.port
        };
        let docker=await DockerModel.findOne(obj);
        if(!docker){
            await DockerModel.create({
                ip:this.ip,
                port:this.port,
                type:this.type,
                onlineUserCount:0,
                v:config.v
            });
        }
    }
}

new Docker();